// Kony


#include "AbilitySystem/Abilitys/BaseBeamSpell.h"

#include "AbilitySystem/BaseAbilitySystemLibrary.h"
#include "GameFramework/Character.h"
#include "Interaction/CombatInterface.h"
#include "Kismet/KismetStringLibrary.h"
#include "Kismet/KismetSystemLibrary.h"

//存储鼠标点击信息
void UBaseBeamSpell::StoreMouseDataInfo(const FHitResult& HitResult)
{
	if (HitResult.bBlockingHit)
	{
		MouseHitLocation = HitResult.ImpactPoint; //存储鼠标点击位置
		MouseHitActor = HitResult.GetActor(); //存储鼠标点击的目标
	}else
	{
		//取消能力
		CancelAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true);
	}
}
//存储玩家控制器
void UBaseBeamSpell::StoreOwnerVariables()
{
	if (CurrentActorInfo)
	{
		//获取并存储当前的玩家控制器
		OwnerPlayerController = CurrentActorInfo->PlayerController.Get();
		//获取并存储当前的玩家角色
		OwnerCharacter = Cast<ACharacter>(CurrentActorInfo->AvatarActor.Get());
	}
}
//计算获取法术碰到的第一个目标
void UBaseBeamSpell::TraceFirstTarget(const FVector& BeamTargetLoction)
{
	check(OwnerCharacter)
	if (OwnerCharacter->Implements<UCombatInterface>())
	{
		//战斗接口获取武器组件
		if (USkeletalMeshComponent* Weapon = ICombatInterface::Execute_GetSocketComponent(OwnerCharacter, "Weapon"))
		{
			//用于忽略角色的数组
			TArray<AActor*> ActorsToIgnore;
			ActorsToIgnore.Add(OwnerCharacter);
			//命中结果
			FHitResult HitResult;
			//获取武器尖端插槽位置
			const FVector SocketLoction = Weapon->GetSocketLocation(FName("TipSocket"));
			//创建单次球体追踪 计算 发射插槽和目标之间的目标
			UKismetSystemLibrary::SphereTraceSingle(OwnerCharacter,
				SocketLoction,
				BeamTargetLoction,
				10.f,
				TraceTypeQuery1,
				false,
				ActorsToIgnore,
				EDrawDebugTrace::None, //ForDuration 是绘制调试信息，None是不绘制
				HitResult,
				true);
			//如果检测中间存在障碍物
			if (HitResult.bBlockingHit)
			{
				//重置鼠标命中位置 到碰撞点
				MouseHitLocation = HitResult.ImpactPoint;
				MouseHitActor = HitResult.GetActor();
			}
		}
	}
	//判断当前点击的目标是否实现了战斗接口
	if (ICombatInterface* CombatInterface = Cast<ICombatInterface>(MouseHitActor))
	{
		//检查主要目标是否绑定过委托，避免重复执行
		if (!CombatInterface->GetOnDeathDelegate().IsAlreadyBound(this, &UBaseBeamSpell::PrimaryTargetDied))
		{
			//绑定角色死亡监听委托,回调是到了蓝图实现事件 ，C++中并没有代码逻辑
			CombatInterface->GetOnDeathDelegate().AddDynamic(this, &UBaseBeamSpell::PrimaryTargetDied);
		}
	}
}
//添加附近额外的目标 用于连锁
void UBaseBeamSpell::StoreAdditionalTargets(TArray<AActor*>& OutAdditionalTarget)
{
	//需要忽略的目标数组
	TArray<AActor*> ActorsToIgnore;
	ActorsToIgnore.Add(GetAvatarActorFromActorInfo());
	ActorsToIgnore.Add(MouseHitActor);
	//获取到的目标数组
	TArray<AActor*> OverlappingActors;
	//获取范围内活着的敌人,赋值给 OverlappingActors
	UBaseAbilitySystemLibrary::GetLivePlayersWithinRadius(
		GetAvatarActorFromActorInfo(),
		OverlappingActors,
		ActorsToIgnore,
		MaxTargetRange, //范围半径
		MouseHitActor->GetActorLocation());
	//数量根据当前等级和最大值计算最小数量
	int NumAdditionalTargets = FMath::Min(GetAbilityLevel()-1,MaxNumShockTargets);
	// int NumAdditionalTargets = 5;
	UBaseAbilitySystemLibrary::GetClosestTargets(
		NumAdditionalTargets,
		OverlappingActors,
		OutAdditionalTarget,
		MouseHitActor->GetActorLocation()
		);
	//遍历所有额外目标
	for (AActor* Target : OutAdditionalTarget)
	{
		//判断当前额外目标是否实现了战斗接口
		if (ICombatInterface* CombatInterface = Cast<ICombatInterface>(Target))
		{
			//检查额外目标是否绑定过委托，避免重复执行
			if (!CombatInterface->GetOnDeathDelegate().IsAlreadyBound(this, &UBaseBeamSpell::AdditionalTargetDied))
			{
				//绑定角色死亡监听委托,回调是到了蓝图实现事件 ，C++中并没有代码逻辑
				CombatInterface->GetOnDeathDelegate().AddDynamic(this, &UBaseBeamSpell::AdditionalTargetDied);
			}
		}
		
	}
}

//当前技能描述
FString UBaseBeamSpell::GetDescription(int32 Level, const FText& Title, const FText& Description)
{
	const float ScaledDamage = Damage.GetValueAtLevel(Level);
	TArray<FStringFormatArg> Args;
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), ScaledDamage)));
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), FMath::Abs(GetManaCost(Level)))));
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), GetCooldown(Level))));
	Args.Add(FStringFormatArg(Level));
	Args.Add(FStringFormatArg(FMath::Min(Level, MaxNumShockTargets)));
	
	return FormatAbilityDescription(Level, Title, Description, Args);
}
//下一级技能描述
FString UBaseBeamSpell::GetNextLevelDescription(int32 Level, const FText& Title, const FText& Description)
{
	const float ScaledDamage = Damage.GetValueAtLevel(Level);
	TArray<FStringFormatArg> Args;
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), ScaledDamage)));
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), FMath::Abs(GetManaCost(Level)))));
	Args.Add(FStringFormatArg(FString::Printf(TEXT("%.1f"), GetCooldown(Level))));
	Args.Add(FStringFormatArg(Level));
	Args.Add(FStringFormatArg(FMath::Min(Level, MaxNumShockTargets)));
	
	return FormatNextLevelDescription(Level, Title, Description, Args);
}
